#include<stdlib.h>
#include<stdio.h>
#include <sys/time.h>



#define CREATOR "grupo1A"
#define RGB_COMPONENT_COLOR 255


#define TIPO unsigned char 
#define TILE_DIM_H 32
#define TILE_DIM_W 16
#define THREADPERBLOCK 512



#define TILE_WIDTH  32
#define REAL_TILE 22 

typedef struct PPMImage
{
   int x,y;
   unsigned  char *R;
   unsigned  char *G;
   unsigned  char *B;


}PPMImage;




unsigned   char * alocar(int linha, int coluna) // funcao para alocaeco dinamica 
{   unsigned   char *v;
    int i;

    if ((v = ( unsigned  char*) malloc(linha*coluna* sizeof(char))) ==  NULL)
      {
         printf("\n\n ERROR - ALOCACAO MEMORIA ");
         system("pause");
         exit (1);
      }

     return v;
}

static PPMImage *readPPM(const char *filename)
{ int i,j,index=0;
  char buff[16];
  PPMImage *img;
  FILE *fp;
  int c, rgb_comp_color;
  
  fp = fopen(filename, "rb");
  if (!fp) {
    fprintf(stderr, "Unable to open file '%s'\n", filename);
    exit(1);
  }

  if (!fgets(buff, sizeof(buff), fp)) {
    perror(filename);
    exit(1);
  }

  if (buff[0] != 'P' || buff[1] != '6') {
    fprintf(stderr, "Invalid image format (must be 'P6')\n");
  exit(1);
  }

  img = (PPMImage *)malloc(sizeof(PPMImage));
  if (!img) {
    fprintf(stderr, "Unable to allocate memory\n");
    exit(1);
  }

  c = getc(fp);
  while (c == '#') {
    while (getc(fp) != '\n') ;
    c = getc(fp);
  }

  ungetc(c, fp);
  if (fscanf(fp, "%d %d", &img->x, &img->y) != 2) {
    fprintf(stderr, "Invalid image size (error loading '%s')\n", filename);
    exit(1);
  }

  if (fscanf(fp, "%d", &rgb_comp_color) != 1) {
    fprintf(stderr, "Invalid rgb component (error loading '%s')\n", filename);
    exit(1);
  }

  if (rgb_comp_color!= RGB_COMPONENT_COLOR) {
    fprintf(stderr, "'%s' does not have 8-bits components\n", filename);
    exit(1);
  }

  while (fgetc(fp) != '\n') ;
  img->R = alocar(img->x,img->y);
  img->G = alocar(img->x,img->y);
  img->B = alocar(img->x,img->y);
  if (!img) {
    fprintf(stderr, "Unable to allocate memory\n");
    exit(1);
  }


  do
  {
    for (i=0;i<img->x ;i++)
    {
      for (j=0;j<img->y ;j++)
      { index=i*img->y+j;
        fread(&img->R[index],sizeof(char),1,fp);
        fread(&img->G[index],sizeof(char),1,fp);
        fread(&img->B[index],sizeof(char),1,fp);
        img->G[index] =0;
        img->B[index]=0;
      }
    }
  } while (feof(fp) != 0 );
  fclose(fp);
  return img;
}



void writePPM(const char *filename, PPMImage *img)
{ int i,j,index;
  FILE *fp;
  fp = fopen(filename, "wb");
  if (!fp) {
    fprintf(stderr, "Unable to open file '%s'\n", filename);
    exit(1);
  }
  fprintf(fp, "P6\n");
  //comments
  fprintf(fp, "# Created by %s\n",CREATOR);

  fprintf(fp, "%d %d\n",img->x,img->y);

  fprintf(fp, "%d\n",RGB_COMPONENT_COLOR);
  // SAIDA DA NOVA IMAGEM
         for (i=0;i< img->x ;i++)
             { 
                  for (j=0;j<  img->y ;j++)
                    {
                       index = i*img->y+ j;
                        fwrite(&img->R[index],sizeof(char),1,fp);
                        fwrite(&img->G[index],sizeof(char),1,fp);
                        fwrite(&img->B[index],sizeof(char),1,fp);

                    }

              }


  fclose(fp);
}



__global__ void cdConv_cd(TIPO* dI, int wI, int hI,
                          TIPO* dR){
 
 
     int Col = blockIdx.x * blockDim.x + threadIdx.x;
     int Row = (blockIdx.y * blockDim.y) + threadIdx.y ;
     int pX,pY;
 
     float v = 0;


   if (Col >= wI || Row >= hI) return;
 
     for( int r = -(int)(5/2) ; r <= (int)(5/2) ; r++ ){
 
         pY = ((Row+r+hI)%hI)*wI;
 
         for( int c = -(int)(5/2) ; c <= (int)(5/2) ; c++ ){
 
             pX = (Col+c+wI)%wI;
 
             v = v+ dI[ pY+pX ];
         }
     }
     dR[(Row*wI)+Col]=  v/25;
     v=0;
 }




/*

__global__ void conv2D_kernel(unsigned char *a, int height, int width, TIPO * out)
{
    __shared__ TIPO  data[TILE_WIDTH][TILE_WIDTH];

    int x = REAL_TILE * blockIdx.x + threadIdx.x - 1;
    int y = REAL_TILE  * blockIdx.y + threadIdx.y - 1;
    int idx = y*width + x;

    if (x < 0 || y < 0 || x >= width || y >= height)
    {
        data[threadIdx.y][threadIdx.x] = 0; // zeroed border
    }
    else
    {
        data[threadIdx.y][threadIdx.x] = a[idx];
    }

    __syncthreads();

    if (x < 0 || y < 0 || x >= width || y >= height ||
        threadIdx.x == 0 || threadIdx.x == TILE_WIDTH - 1 ||
        threadIdx.y == 0 || threadIdx.y == TILE_WIDTH - 1)
        return;

    float value = 0;

    for (int dx = -1, mx = 1; dx < 2; dx++, mx--)
    {
        for (int dy = -1, my = 1; dy < 2; dy++, my--)
        {
            value += data[threadIdx.y + dy][threadIdx.x + dx] ;
        }
    }

    out[idx] = value;


}

*/







int main(int argc , char*argv[])
{
  struct timeval utime;
  double tInicio, tFim;


   PPMImage *imagem,
             saida ;

    imagem = readPPM(argv[1]);


    saida.x= imagem->x;
    saida.y= imagem->y;
    saida.R = alocar( saida.x, saida.y );
    saida.G = alocar( saida.x, saida.y );
    saida.B = alocar( saida.x, saida.y );
  


    writePPM("saida_igual_entrada.ppm",imagem);

    printf(" bebs gay ...\n");
 //   getchar();

    gettimeofday(&utime,NULL);
    tInicio = utime.tv_sec + ( utime.tv_usec / 1000000.0);


     TIPO *I= imagem->R;
     int wI = imagem->x;
     int hI = imagem->y;

     unsigned char *R;
     R = saida.R  ;

     TIPO *dI;
     TIPO *dR;

     cudaMalloc((void **)&dI, wI*hI * sizeof(TIPO));

     cudaMalloc((void **)&dR, wI*hI * sizeof(TIPO));

     cudaMemcpy(dI, I, wI*hI * sizeof(TIPO), cudaMemcpyHostToDevice);

     cudaMemset(dR, 0, wI*hI * sizeof(TIPO));
   //  getchar();
     int fixedGridW = (wI % TILE_DIM_W == 0)? wI/TILE_DIM_W : wI/TILE_DIM_W+1;
     int fixedGridH = (hI % TILE_DIM_H == 0)? hI/TILE_DIM_H : hI/TILE_DIM_H+1;
     dim3 gridDim(fixedGridW,fixedGridH );
     dim3 blockDim(TILE_DIM_W,TILE_DIM_H);
//     printf("%d %d /n", wI,hI);




//           conv2D_kernel<<<gridDim, blockDim>>>(dI, wI, hI, dR);

             cdConv_cd<<<gridDim, blockDim>>>(dI,wI,hI,dR);

     cudaMemcpy(R, dR, wI*hI * sizeof(TIPO), cudaMemcpyDeviceToHost);


     gettimeofday(&utime,NULL);
     tFim = utime.tv_sec + ( utime.tv_usec /1000000.0);
     printf("%.4lf - Tempo percorrido\n", tFim - tInicio); 

     cudaFree(dR);
     cudaFree(dI);


    writePPM("saida.ppm",&saida);

 return 0;
}
